package com.changanw.middleware.mybatis.boot;

import com.changanw.middleware.mybatis.SqlSessionFactory;
import com.changanw.middleware.mybatis.SqlSessionFactoryBuilder;
import com.changanw.middleware.mybatis.spring.MapperFactoryBean;
import com.changanw.middleware.mybatis.spring.MapperScannerConfigurer;
import com.changanw.middleware.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author changanw
 * @description
 * @date 2024/2/28 16:57
 */
@Configuration
@ConditionalOnClass({SqlSessionFactory.class})
@EnableConfigurationProperties(MybatisProperties.class)
//@EnableConfigurationProperties(MybatisProperties.class)，用于引入配置信息，如果你不引入是不能使用配置信息的。
public class MybatisAutoConfiguration implements InitializingBean {
  private Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
  /**
   * 创建sqlSessionFactory的bean
   * 实例化 SqlSession 的链接工厂，
   * 首先可以看到入参信息包括了我们的这里创建的 connection，另外还有配置入参信息。
   * @param connection
   * @param mybatisProperties
   * @return
   * @throws Exception
   */


  @Bean
  //@ConditionalOnBean(Connection.class)
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(MybatisProperties mybatisProperties) throws Exception {
    logger.info("创建sqlSessionFactory");
    if (mybatisProperties.getDriver() != null){
      try {
        Class.forName(mybatisProperties.getDriver());
        Connection connection = DriverManager.getConnection(mybatisProperties.getUrl(), mybatisProperties.getUsername(), mybatisProperties.getPassword());
        return new SqlSessionFactoryBuilder().build(connection, mybatisProperties.getMapperLocations());
      } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
      }
    }
    return null;
  }

  /**
   * 连接数据库的bean
   * 实例化数据库连接，mybatisProperties 是这个方法的入参，
   * 这里也可以通过属性注入方法体里使用的方式进行处理。
   * 另外，这里返回的数据库连接方式，是我们从类 SqlSessionFactoryBuilder  提取出来的，
   * 这样的方式也非常符合官网中 mybaits SpringBoot Starter 的处理方式，因为这样可以更加方便的增加其它的数据库连接池。
   * @param mybatisProperties
   * @return
   */
  @Bean
  @ConditionalOnMissingBean
  public Connection connection(MybatisProperties mybatisProperties) {
    //加入判空操作，防止没有写配置文件报错
    if (mybatisProperties.getDriver() != null){
      try {
        Class.forName(mybatisProperties.getDriver());
        return DriverManager.getConnection(mybatisProperties.getUrl(), mybatisProperties.getUsername(), mybatisProperties.getPassword());
      } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
      }
    }

    return null;
  }

  /**
   * 内部类 AutoConfiguredMapperScannerRegistrar 的实现，注册 Bean 信息是优先于 Bean 实例化的，
   * 所以这里需要通过实现 EnvironmentAware
   * 读取 yml 中的配置信息，因为这个配置信息是用于扫描 DAO 接口类，注册 Bean 的。
   */
  public static class AutoConfiguredMapperScannerRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar {

    private String basePackage;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
      builder.addPropertyValue("basePackage", basePackage);
      registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
    }

    @Override
    public void setEnvironment(Environment environment) {
      this.basePackage = environment.getProperty("mybatis.datasource.base-dao-package");
    }
  }

  /**
   * 它的作用是把我们的用于扫描加载配置和初始化的类启动起来。
   * 主要通过引入
   * @Import(AutoConfiguredMapperScannerRegistrar.class)
   * 在实现了 InitializingBean 类上，就可以被 Spring 处理了。
   */
  @Configuration
  @Import(AutoConfiguredMapperScannerRegistrar.class)
  @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
    }

  }
  @Override
  public void afterPropertiesSet() throws Exception {

  }
}
